#include "LibGlobal.h"

#define DEFAULT_TIMEOUT_WAIT_NETWORK     3 * 60 * 1000
#define DEFAULT_TIMEOUT_CREATE_SOCKET    30 * 1000
#define DEFAULT_TIMEOUT_CONNECT_SOCKET   30 * 1000
#define DEFAULT_TIMEOUT_RECV_DATA        30 * 1000

#define GOTO_STATUS(instance, status) instance->mTaskStatus = status

static s32 handleIdleStatus(AsyncTaskInstance *instance)
{
    if (instance->mStartTaskTimeId == 0)
    {
        instance->mStartTaskTimeId = sdkTimerGetId();
    }
    if (sdkTimerIsEnd(instance->mStartTaskTimeId, instance->mNetWorkTimeout))
    {
        TraceE("wait network timeout . \r\n");
        return SDK_TIME_OUT;
    }
    return isCommEnable() ? SDK_OK : SDK_EQU;
}

static s32 handleCreateSocketStatus(AsyncTaskInstance *instance)
{
    s32 ret = ZERO;

    if (instance->mStartCreateSocketTimeId == 0)
    {
        instance->mStartCreateSocketTimeId = sdkTimerGetId();
    }
    if (sdkTimerIsEnd(instance->mStartCreateSocketTimeId, instance->mCreateSocketTimeout))
    {
        TraceE("create socket timeout . \r\n");
        return SDK_TIME_OUT;
    }
    sdkCommPreById(&instance->mSocketInfo);
    ret = sdkCommCreateLinkById(&instance->mSocketInfo, SOCKET_TYPE_TCP, instance->mIP, instance->mPort);
    if (ret == SDK_EQU && instance->mSocketInfo.socketId == 0)
    {
        return SDK_EQU;
    }
    else if (instance->mSocketInfo.socketId != 0)
    {
        return SDK_OK;
    }
    else
    {
        TraceE("create link err ret = %d \r\n", ret);
        return SDK_ERR;
    }
}

static s32 handleConnectSocketStatus(AsyncTaskInstance *instance)
{
    s32 socketStatus = ZERO;

    if (instance->mStartConnectSocketTimeId == 0)
    {
        instance->mStartConnectSocketTimeId = sdkTimerGetId();
    }

    if (sdkTimerIsEnd(instance->mStartConnectSocketTimeId, instance->mConnectSocketTimeout))
    {
        TraceE("connect socket timeout . \r\n");
        return SDK_TIME_OUT;
    }
    socketStatus = sdkCommGetSocketStatusById(&instance->mSocketInfo);
    if (socketStatus == SDK_COMM_CONNECTING)
    {
        return SDK_EQU;
    }
    else if (socketStatus == SDK_COMM_CONNECTED)
    {
        return SDK_OK;
    }
    else
    {
        TraceE("create socket err, status = %d \r\n", socketStatus);
        return SDK_ERR;
    }
}

static s32 handleSendDataStatus(AsyncTaskInstance *instance)
{
    s32 ret = ZERO;

    ret = instance->mWriteDataFunc();
    if (ret != SDK_OK)
    {
        TraceE("write data err ret = %d \r\n", ret);
        return SDK_ERR;
    }
    return SDK_OK;
}

static s32 handleRecvDataStatus(AsyncTaskInstance *instance)
{
    s32 ret = ZERO;

    if (instance->mStartReadTimeId == 0)
    {
        instance->mStartReadTimeId = sdkTimerGetId();
    }
    if (sdkTimerIsEnd(instance->mStartReadTimeId, instance->mReadTimeout))
    {
        return SDK_TIME_OUT;
    }

    if (instance->mReadBufLen >= instance->mReadBufMaxLen)
    {
        TraceE("recv err readBufLen = %d maxReadBufLen = %d \r\n", instance->mReadBufLen, instance->mReadBufMaxLen);
        return SDK_ERR;
    }

    ret = sdkCommRecvById(&instance->mSocketInfo, instance->mReadBuf + instance->mReadBufLen, instance->mReadBufMaxLen - instance->mReadBufLen);
    if (ret < 0)
    {
        TraceE("rev err ret = %d \r\n", ret);
        return SDK_ERR;
    }
    instance->mReadBufLen += ret;

    return instance->mGetReadDataStatusFunc(instance->mReadBuf, instance->mReadBufLen);
}

static s32 handleRecvDataEndStatus(AsyncTaskInstance *instance)
{
    return instance->mHandleReadDataFunc(instance->mReadBuf, instance->mReadBufLen);
}

static s32 handleDestroyStatus(AsyncTaskInstance *instance)
{
    instance->mOnEndTaskFunc();
    stopAsyncTask(instance);
    return SDK_OK;
}

static bool isErrTaskInstance(AsyncTaskInstance *instance)
{
    if (instance == NULL)
    {
        return true;
    }
    if (instance->mReadBuf == NULL)
    {
        return true;
    }
    if (strlen(instance->mIP) == 0 || strlen(instance->mPort) == 0)
    {
        return true;
    }
    if (instance->mNetWorkTimeout == 0 || instance->mCreateSocketTimeout == 0 || instance->mConnectSocketTimeout == 0 || instance->mReadTimeout == 0)
    {
        return true;
    }
    if (instance->mWriteDataFunc == NULL || instance->mGetReadDataStatusFunc == NULL || instance->mHandleReadDataFunc == NULL
        || instance->mOnEndTaskFunc == NULL || instance->mOnLoopListener == NULL)
    {
        return true;
    }
    return false;
}

void startAsyncTask(AsyncTaskInstance *instance)
{
    if (isErrTaskInstance(instance))
    {
        if (instance != NULL && instance->mOnEndTaskFunc != NULL)
        {
            instance->mOnEndTaskFunc();
        }
        return;
    }
    sdkRegisterLoopListener(instance->mOnLoopListener);
}

void stopAsyncTask(AsyncTaskInstance *instance)
{
    if (instance != NULL && instance->mOnLoopListener != NULL)
    {
        sdkUnRegisterLoopListener(instance->mOnLoopListener);
    }
}

void executeAsyncTask(AsyncTaskInstance *instance)
{
    s32 ret = ZERO;

    if (instance == NULL)
    {
        handleDestroyStatus(instance);
        return;
    }

    if (!sdkTimerIsEnd(instance->mLoopExecuteTimeId, 100))
    {
        return;
    }

    instance->mLoopExecuteTimeId = sdkTimerGetId();

    switch (instance->mTaskStatus)
    {
        case ASYNC_TASK_STATUS_IDLE:
            ret = handleIdleStatus(instance);
            if (ret != SDK_EQU)
            {
                GOTO_STATUS(instance, ret == SDK_OK ? ASYNC_TASK_STATUS_CREATE_SOCKET : ASYNC_TASK_STATUS_DESTROY);
            }
            break;

        case ASYNC_TASK_STATUS_CREATE_SOCKET:
            ret = handleCreateSocketStatus(instance);
            if (ret != SDK_EQU)
            {
                GOTO_STATUS(instance, ret == SDK_OK ? ASYNC_TASK_STATUS_CONNECT_SOCKET : ASYNC_TASK_STATUS_DESTROY);
            }
            break;

        case ASYNC_TASK_STATUS_CONNECT_SOCKET:
            ret = handleConnectSocketStatus(instance);
            if (ret != SDK_EQU)
            {
                GOTO_STATUS(instance, ret == SDK_OK ? ASYNC_TASK_STATUS_SEND : ASYNC_TASK_STATUS_DESTROY);
            }
            break;

        case ASYNC_TASK_STATUS_SEND:
            ret = handleSendDataStatus(instance);
            GOTO_STATUS(instance, ret == SDK_OK ? ASYNC_TASK_STATUS_RECV : ASYNC_TASK_STATUS_DESTROY);
            break;

        case ASYNC_TASK_STATUS_RECV:
            ret = handleRecvDataStatus(instance);
            if (ret != SDK_EQU)
            {
                GOTO_STATUS(instance, ret == SDK_OK ? ASYNC_TASK_STATUS_HANDLE : ASYNC_TASK_STATUS_DESTROY);
            }
            break;

        case ASYNC_TASK_STATUS_HANDLE:
            handleRecvDataEndStatus(instance);
            GOTO_STATUS(instance, ASYNC_TASK_STATUS_DESTROY);
            break;

        case ASYNC_TASK_STATUS_DESTROY:
            handleDestroyStatus(instance);
            break;

        default:
            break;
    }
}

s32 buildAsyncTaskInstance(AsyncTaskInstance *instance, s8 *ip, s8 *port, u32 recvMaxLen)
{
    if (instance == NULL)
    {
        return LIB_ERR_PARAM;
    }

    instance->mReadBuf = sdkGetPersistentMem(recvMaxLen);
    if (instance->mReadBuf == NULL)
    {
        return LIB_ERR_MALLOC;
    }

    memset(instance->mReadBuf, 0x00, recvMaxLen);
    instance->mReadBufMaxLen = recvMaxLen;
    snprintf(instance->mIP, sizeof(instance->mIP), "%s", ip);
    snprintf(instance->mPort, sizeof(instance->mPort), "%s", port);
    setAsyncTaskInstanceTimes(instance, DEFAULT_TIMEOUT_WAIT_NETWORK, DEFAULT_TIMEOUT_CREATE_SOCKET, DEFAULT_TIMEOUT_CONNECT_SOCKET, DEFAULT_TIMEOUT_RECV_DATA);
    return SDK_OK;
}

void setAsyncTaskInstanceFunc(AsyncTaskInstance *instance, WriteDataFunc writeFunc, GetReadDataStatusFunc getStatusFunc,
                              HandleReadDataFunc handleFunc, OnEndTaskFunc endTaskFunc, onLoopListener listener)
{
    if (instance == NULL)
    {
        return;
    }

    instance->mWriteDataFunc = writeFunc;
    instance->mGetReadDataStatusFunc = getStatusFunc;
    instance->mHandleReadDataFunc = handleFunc;
    instance->mOnEndTaskFunc = endTaskFunc;
    instance->mOnLoopListener = listener;
}

void setAsyncTaskInstanceTimes(AsyncTaskInstance *instance, u32 netWorkTimeout, u32 createSocketTimeout, u32 connectSocketTimeout, u32 recvTimeout)
{
    if (instance == NULL)
    {
        return;
    }

    instance->mNetWorkTimeout = netWorkTimeout;
    instance->mCreateSocketTimeout = createSocketTimeout;
    instance->mConnectSocketTimeout = connectSocketTimeout;
    instance->mReadTimeout = recvTimeout;
}

void freeAsyncTaskInstance(AsyncTaskInstance *instance)
{
    if (instance == NULL)
    {
        return;
    }

    if (instance->mReadBuf != NULL)
    {
        sdkFreePersistentMem(instance->mReadBuf);
        instance->mReadBuf = NULL;
    }

    if (instance->mSocketInfo.socketId != 0)
    {
        sdkCommDestroyLinkById(&instance->mSocketInfo);
        instance->mSocketInfo.socketId = 0;
    }
}